Visual Form

Design is a requirement, not a cosmetic addition.


  • Nützlichkeit
  • Effizienz
  • Ästhetik

Foto von Samuel Scrimshaw auf Unsplash

Effizienz: Salienz von Informationen

Durch die Unterscheidbarkeit von grafischen Symbolen (visual features) soll grafische Informationen möglichst schnell aufgenommen werden, ohne lange überlegen zu müssen.

Dafür können die sog. Gestalt-Prinzipien genutzt werden.

Menschen nehmen Muster nach gewissen Prinzipien wahr. Die Regeln, nach denen wir Muster wahrnehmen, werden Gestalt-Prinzipien genannt, beispielsweise:

  • Nähe: Einander nahe Elemente werden als zusammengehörig wahrgenommen
  • Ähnlichkeit: Einander ähnliche Elemente werden als zusammengehörig wahrgenommen
  • Verbundene Elemente: Verbundene Elemente werden als zusammengehörig wahrgenommen
  • Gute Fortsetzung: Linien werden wahrgenommen, als folgten sie dem einfachsten Weg
  • Geschlossenheit: Unvollständige Elemente werden als vollständig wahrgenommen
  • Figur und Grund: Elemente sind entweder im Vorder- oder Hintergrund
  • Gemeinsames Schicksal: Elemente mit ähnlicher Bewegungsrichtung werden als zusammengehörig wahrgenommen

Salienz

Salienz durch visual features:

Quelle: Wong, B. (2010)

Gruppieren

Gruppierung durch Form

Gruppierung durch verbundene Elemente

Darstellung von Größen

Die Größe von Elemenen kann auch genutzt werden, um Daten zu kodieren.

Einschätzen von Größen

Die Größe von Kreisen einzuschätzen ist schwer!

Das gleiche gilt für Winkel!

Versuche, die Größen zu sortieren:

Das gleiche gilt für Winkel!

Kreis- bzw. Tortendiagramme sollten daher vermieden werden.

Farben

Farbe ist eine der wichtigsten Methoden, um Informationen zu [labeln], hervorzuheben, [Werte] zu kommunizieren, [Realität simulieren], und um zu designen und zu [dekorieren].

Foto von Efe Kurnaz auf Unsplash

Kategoriale vs. quantitative Daten

Kategoriale Daten

Verschiedene Farben, die sich gut unterscheiden lassen.

Quantitative Daten

Farbverläufe, die eine Reihenfolge haben.

ggplot2 setzt die entsprechende Skala schon automatisch!

Skalen mit Nullpunkt

  • Was zu beachten ist: unten Blau oben Rot … https://ggplot2-book.org/scales-colour.html

  • Vor allem für diskrete Gruppen geeignet

  • Farbverläufe schwieriger (aber es gibt color scales dafür)

  • Bestimmte Farben (Grün = gut, Rot = Gefahr)

  • Intervallskaliert/Verhältnisskaliert: Es gibt Nullpunkt: Divergierende Skalen

    • Kein Nullpunkt: Gleichmäßige Abstände zwischen Farbwerten
  • Ordinal: Richtung und gute Unterscheidbarkeit im Farbraum

  • Nominalskaliert: Spriale/Diagnoale im Farbraum

  • Diskret

  • Continuous

  • Binned Vorgefertigte Paletten:

Sequenziell
Qualitativ
Diverging

Farbwahrnehmung beim Menschen

Primärfarben des Lichts: Rot, Grün, Blau


By Original: Penubag Vector: Victor Blacus - Own work based on: Electromagnetic-Spectrum.png, CC BY-SA 3.0, Link
By BenRG - Own work, Public Domain, Link

Andere Tiere können auch mal mehr oder weniger haben:

Bild aus Farbsehen der Tiere
Foto von Ben Moreland auf Unsplash

Viele Tiere sehen nur grau

Warum überhaupt Farbwahrnehmung?

  • Konsistenz: Farbtöne sind unabhängig vom Licht wahrnehmbar.
  • Aber: More is not always better! Zu viel kann auch vom Wesentlichen ablenken.

Farbwahrnehmung ist also subjektiv, und bringt nur einen Vorteil, wenn eine Bedeutung daraus abgeleitet werden kann.

Foto von Chinh Le Duc auf Unsplash

More is not always better

ggplot(
  gapminder_2020,
  aes(x = gini, y = hapiscore_whr, color = world_4region)
) +
  geom_point(size = 3) +
  theme_bg() 

Mehr als sechs Farben sollten es auf keinen Fall werden!

Alternative: Facetting.

Farben

Ganglienzellen übertragen keine Informationen über absolute Helligkeit, sondern über relative Helligkeit.

Visualisierung kann nicht gut darin sein, absolute Werte zu kommunizieren (Tabellen können das besser). Aber: Unterschiede und Veränderungen.

Bild von Nature Methods.

Hier sind die Farben klar unterscheidbar, und der Gini-Index lässt sich gut ablesen.

Der Plot ist etwas sub-optimaler. Die tatsächlichen Werte des Gini-Index werden nicht gut kommuniziert.

Farbe ist relativ

Eigenschaften von Farben

Farbton, Sättigung, Helligkeit

Die drei Eigenschaften beschreiben das Aussehen von Farben.

Farbräume

  • RGB: Zusammenmischen von Licht der unterschiedlichen Farben. Alle drei zusammen ergeben weiß.
  • CMYK: Ausblenden aus dem weißen Farbspektrum durch Filter. Grundfarben: Gelb, Magenta (Purpur), Cyan (Blaugrün) (+K - Key (also Schwarz))

Salienz

Sticht eine Farbe hervor, ist sie besonders salient - das kann gewollt oder ungewollt sein.

Größe

Farbfehlsichtigkeit

protanopic right side

By Mark Fairchild, CC BY-SA 3.0, Link

  • Protanopie: Zapfentyp L (rot) fehlt: rot vs grün Schwäche

  • Deuteranopie: Zapfentyp M (grün) fehlt: rot vs grün Schwäche

  • Tritanopie: Zapfentyp S (blau) fehlt: blau vs grün, gelb vs violett Schwäche

  • Take the above scale and show that it is not great for color blindness

  • https://www.color-blindness.com/coblis-color-blindness-simulator/

Colorblind-Check

Auf Colblindor.

Mit colorblindcheck.

library(colorblindcheck)

palette_check(scales::hue_pal()(6), plot = TRUE)

          name n tolerance ncp ndcp  min_dist mean_dist max_dist
1       normal 6  28.22768  15   15 28.227682  48.60479 91.99894
2 deuteranopia 6  28.22768  15    9  5.870477  31.50134 59.55026
3   protanopia 6  28.22768  15    8  3.930855  32.79118 57.04317
4   tritanopia 6  28.22768  15    9  7.200490  40.41105 71.61725

Anwendung in ggplot2

Farben wählen

  • Name
  • rgb()
  • Nummer
  • Hex code

Farben manuell setzen

ggplot(
  continent_co2_pct,
  aes(
    x = world_6region,
    y = percentage,
    fill = world_6region
  )
) +
  geom_bar(stat = "identity", width = 1) +
  theme_minimal() +
  labs(title = "Share of global per capita CO2 consumption") +
  scale_fill_manual(values = c("red", "green", "blue", "yellow", "violet", "brown"))

my_color_scale <- c(
  "East Asia & Pacific" = "red",
  "South Asia" = "green",
  "Sub-Saharan Africa" = "blue",
  "Europe & Central Asia" = "yellow",
  "Middle East & North Africa" = "violet",
  "Americas" = "brown"
)

ggplot(
  continent_co2_pct,
  aes(
    x = world_6region,
    y = percentage,
    color = world_6region
  )
) +
  geom_bar(stat = "identity", width = 1) +
  theme_minimal() +
  labs(title = "Share of global per capita CO2 consumption 2020") +
  scale_color_manual(values = my_color_scale)

Wenn du in einem Projekt die gleichen Gruppen in verschiedenen Plots zeigst, setze die Farbskala einmal mit den Gruppennamen, damit die Gruppen in allen Plots die selben Farben bekommen.

Graustufen

ggplot(
  continent_co2_pct,
  aes(
    x = world_6region,
    y = percentage,
    fill = world_6region
  )
) +
  geom_bar(stat = "identity", width = 1) +
  theme_minimal() +
  labs(title = "Share of global per capita CO2 consumption 2020") +
  scale_fill_grey()

Externe Farb-Paletten

R Color Brewer

library(RColorBrewer)
display.brewer.all(colorblindFriendly = FALSE)

library(RColorBrewer)

ggplot(
  continent_co2_pct,
  aes(
    x = world_6region,
    y = percentage,
    fill = world_6region
  )
) +
  geom_bar(stat = "identity", width = 1) +
  theme_minimal() +
  labs(title = "Share of global per capita CO2 consumption") +
  scale_fill_brewer(palette = "PuOr")

Externe Farb-Paletten

paletteer

Sammlung von Paletten aus diversen Paketen

library(paletteer)

ggplot(
  continent_co2_pct,
  aes(
    x = world_6region,
    y = percentage,
    fill = world_6region
  )
) +
  geom_bar(stat = "identity", width = 1) +
  theme_minimal() +
  labs(title = "Share of global per capita CO2 consumption") +
  scale_fill_paletteer_d("LaCroixColoR::Orange")

Übung

  1. Baue eine eigene Farbpalette für deinen Plot und teste sie auf Farbfehlsichtigkeiten.

Nützlichkeit

Foto von Kelly Sikkema auf Unsplash
Foto von Yucel Moran auf Unsplash

Hier gilt ganz besonders: Simplify to Clarify

Funktionelles Design

Foto von BVG

Foto von BVG

Dabei kann es helfen, sich von bereits vorhandenen Plots inspirieren zu lassen.
Handschriftliche Skizze ist auch erstmal hilfreich!

Simplify to clarify: Data-ink ratio

Data graphics should draw the viewer’s attention to the sense and substance of the data, not to something else. (Tufte & Graves-Morris, 1983)

Auf die Spitze getrieben von Tufte & Graves-Morris (1983):

Data-ink ratio = Data-ink / Total ink used to print the graphic

  • Jedes Stück Tinte sollte einen Grund haben.

:::{.image-ref} CC BY-SA 2.0, Link

:::

Data-ink ratio in Praxis

10% Daten, 90 % Achsenbeschriftungen und Hintergrundlinien

library(ggthemes)

ggplot(
  data = gapminder_2020,
  mapping = aes(x = gini, y = hapiscore_whr, color = world_4region)
) +
  geom_point() +
  theme_excel() +
  scale_x_continuous(
    breaks = scales::pretty_breaks(n = 10), # ensure ticks
    minor_breaks = waiver() # ensures minor ticks too
  ) +
  scale_y_continuous(
    breaks = scales::pretty_breaks(n = 10),
    minor_breaks = waiver()
  ) +
  theme(
    panel.grid.major = element_line(color = "grey60", size = 1.2), # thick major grid
    panel.grid.minor = element_line(color = "grey60", size = 0.8) # thick minor grid
  )

0% Daten (Plotte die Daten, keine Zusammenfassungen)

ggplot(
  data = gapminder_2020,
  mapping = aes(x = gini, y = hapiscore_whr, color = world_4region)
) +
  geom_smooth()

ca. 90 % Daten, 10 % Achsenbeschriftungen und Legende

library(ggthemes)
ggplot(
  data = gapminder_2020,
  mapping = aes(x = gini, y = hapiscore_whr, color = world_4region)
) +
  geom_point() +
  theme_tufte()

Redundanzen

Erase non-data-ink, within reason. Erase redundant data-ink, within reaseon (Tufte & Graves-Morris, 1983)

Auf wie viele Arten wird hier die Höhe der CO2 Emissionen pro Kopf dargestellt?

Auf sieben verschiedene Arten:

  1. Höhe der linken Balkenlinie
  2. Höhe der rechten Balkenlinie
  3. Höhe der horizontalen Balkenlinie
  4. Höhe des Balkeninneren
  5. Farbe des Balkens
  6. Position des Labels
  7. Wert des Labels

Balken an sich enthalten also schon redundante Informationen!

Entferne alle Redundanzen, machen den Plot also “Tufte-Komform”

ggplot(
  gapminder_2020[1:10, ],
  aes(x = reorder(country, co2_pcap_cons), y = co2_pcap_cons, fill = co2_pcap_cons, color = co2_pcap_cons, label = round(co2_pcap_cons), 2)
) +
  geom_bar(stat = "identity", width = 1, color = "black") +
  geom_text(vjust = -1, color = "black") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  labs(
    x = "Land",
    y = "CO2 Consumption per capita in t") +
  theme_bg()

Entfernen der Achsenlinien und Umrandungen. Hintergrundlinien werden innerhalb der Balken dargestellt.

Mitunter können die vertikalen und horizontalen Linien auch hilfreich beim Vergleich/bei der Zuordnung zum Land sein: “Tufte Barchart”

Maximieren von Informationen

Normaler Scatterplot

Box um den Plot, Grid-Linien, Achsen-Ticks: Alles keine Daten.

ggplot(
  data = gapminder_2020,
  mapping = aes(x = gini, y = hapiscore_whr, color = world_4region)
) +
  geom_point() 

Tufte-Scatterplot

Nutzen der Ticks und Achsen-Linien, um Informationen zu transportieren.

library(paletteer)


xlim_v <- c(20, 80)
ylim_v <- c(20, 80)

# Parameter: Anzahl der Quantile, Versatz von den Rändern, kleine Lücke zwischen Segmenten
probs <- seq(0, 1, by = 0.25) # Quartile; für Dezile: by = 0.1
off_x <- 0 # Versatz der "y-Achse" nach rechts
off_y <- 0 # Versatz der "x-Achse" nach oben
gap <- 0.2 # kleine Lücke an Segmentenden (optisch schöner)

qx <- quantile(gapminder_2020$gini, probs = probs, na.rm = TRUE)
qy <- quantile(gapminder_2020$hapiscore_whr, probs = probs, na.rm = TRUE)

# Segment-Dataframes bauen (pro Intervall ein Segment)
seg_x <- tibble(
  x    = pmax(qx[-length(qx)] + gap / 2, xlim_v[1]),
  xend = pmin(qx[-1] - gap / 2, xlim_v[2]),
  y    = ylim_v[1] + off_y,
  yend = ylim_v[1] + off_y
) %>% filter(x < xend)

seg_y <- tibble(
  x    = xlim_v[1] + off_x,
  xend = xlim_v[1] + off_x,
  y    = pmax(qy[-length(qy)] + gap / 2, ylim_v[1]),
  yend = pmin(qy[-1] - gap / 2, ylim_v[2])
) %>%
  filter(y < yend)

ggplot(
  data = gapminder_2020,
  mapping = aes(x = gini, y = hapiscore_whr, color = world_4region)
) +
  geom_point() +
  # Quantil-"x-Achse" (horizontal) leicht ins Panel versetzt
  geom_segment(
    data = seg_x,
    aes(x = x, xend = xend, y = y, yend = yend),
    inherit.aes = FALSE
  ) +
  # Quantil-"y-Achse" (vertikal) leicht ins Panel versetzt
  geom_segment(
    data = seg_y,
    aes(x = x, xend = xend, y = y, yend = yend),
    inherit.aes = FALSE
  ) +
  scale_x_continuous(breaks = unique(c(round(seg_x$x, 0), round(seg_x$xend, 0)))) +
  scale_y_continuous(breaks = unique(c(round(seg_y$y, 0), round(seg_y$yend, 0)))) +
  coord_cartesian(xlim = xlim_v, ylim = ylim_v, expand = FALSE) +
  theme_tufte() +
  theme(axis.ticks = element_blank()) +
  paletteer::scale_colour_paletteer_d("fishualize::Koumansetta_rainfordi")

Verteilungen Plotten




Reduzieren heißt nicht, die Informationen zu reduzieren, sondern den Chart-Junk!
Sehr oft macht es Sinn, die gesamte Verteilung zu plotten, möglicherweise auch zusätzlich zu zusammenfassenden Statistiken.

Verteilungen plotten

Boxplot

ggplot(data = gapminder_2020 %>% drop_na(country),
  aes(x = world_4region, y = hapiscore_whr, fill = world_4region)) +
  geom_boxplot() +
  theme(axis.ticks = element_blank(), legend.position = "none") +
  scale_fill_paletteer_d("fishualize::Koumansetta_rainfordi") +
  xlab("Region") +
  ylab("Happiness Score (World Happiness Report)")

Tufte-Boxplot

library(ggthemes)

ggplot(data = gapminder_2020 %>% drop_na(country), 
  aes(x = world_4region, y = hapiscore_whr, group = world_4region)) +
geom_jitter(alpha = 0.5, color = "lightgrey") + 
  geom_tufteboxplot() +
  scale_fill_paletteer_d("fishualize::Koumansetta_rainfordi") +
  xlab("Region") +
  ylab("Happiness Score (World Happiness Report)") +
theme_tufte()

Keine Angst vor leerem Raum

Zusammenfassung



  1. Above all else show the data.
  2. Maximize the data-ink ratio, within reason.
  3. Erase non-data-ink, within reason.
  4. Erase redundant data-ink, within reason.
  5. Revise and edit.

Aus Tufte & Graves-Morris (1983).

Gegenentwurf: Ästhetik

Abbildung von [Cédric Scherer]https://www.behance.net/gallery/100683383/Travelling-to-Outer-Space#).

Wandelt euren Plot in einen Tufte-Plot um.

Farben als Design-Element

All colors are the friends of their neighbors and the lovers of their opposites. - Marc Chagall

Nach MalteAhrens at de.wikipedia. Vectorization by User:SidShakal - Raster version from Wikimedia Commons., Gemeinfrei, Link

Kleine Farbenlehre

Vincent van Gogh, Public domain, via Wikimedia Commons

  • Gegenüberliegende Farben intensivieren sich gegenseitig: eine Primärfarbe, und eine Sekundärfarbe, die aus den anderen beiden Primärfarben gemischt wird. Das erzeugt stärkere Kontraste. (Van Gogh bild)
  • Mit weiß und schwarz lassen sich die Schattierungen hinzumischen
  • triadisches Farbschema: Fom MIttelpunkt des Farbkreises Strahlen mit gleichem Winkel abgehen lassen (So viele, wie man Farben haben will). Das wählt dann Farben mit dem größten Kontrast aus. (https://www.kunstplaza.de/fashion-design/farbenlehre-farbtheorie-styleguides-fashion-design/)

Weitere Tools

Wollbox von Vincent van Gogh, mit der er Farbkombinationen getestet hat. Eigenes Bild.

Schriften

Die Schriftart kann einer Abbildung ein ganz anderes Aussehn vereleihen.
Wir nutzen Schriftarten von Google Fonts

library(showtext)

font_add_google("Special Elite", family = "special")

Daten von zygmuntz.

library(ggwordcloud)

tags <- read.csv(here::here("data", "goodbooks-10k", "tags.csv"))
book_tags <- read.csv(here::here("data", "goodbooks-10k", "book_tags.csv"))
books <- read.csv(here::here("data", "goodbooks-10k", "books.csv")) 


classics <- book_tags %>% 
left_join(books) %>%
left_join(tags) %>% 
filter(tag_name == "classics") %>% 
filter(ratings_count > 20000, !duplicated(authors)) %>%
  select(original_title, average_rating, tag_name, ratings_count) %>%
  distinct() %>% 
  arrange(ratings_count) %>%
  slice_tail(n = 50)


set.seed(45)
ggplot(data = classics, 
       mapping = aes(label = original_title, size = average_rating)) +
  geom_text_wordcloud() +
scale_size(range = c(1, 6)) +
labs(title = "What to read next ...", 
subtitle = "... according to goodreads") +
theme_bg()

font_add_google("Special Elite", family = "my textfont")
font_add_google("Playfair Display")

showtext_auto()


set.seed(45)
ggplot(data = classics, 
       mapping = aes(label = original_title, size = average_rating)) +
  geom_text_wordcloud(family = "my textfont") +
scale_size(range = c(1, 6)) +
labs(title = "What to read next", 
subtitle = "According to goodreads") +
theme_bg() +
theme(title = element_text(family = "Playfair Display", size = 20), 
)

Suche dir eine Schriftart aus Google Fonts und baue sie in deinen Plot ein.

Übung

Wähle eine Grafik von … aus und erstelle einen Plan, wie du sie verbessern würdest.

Tufte, E. R., & Graves-Morris, P. R. (1983). The visual display of quantitative information (Bd. 2). Graphics press Cheshire, CT.